package service

import (
	"context"
	"gitee.com/DonHz/basic-go/webook/internal/domain"
	events "gitee.com/DonHz/basic-go/webook/internal/events/article"
	"gitee.com/DonHz/basic-go/webook/internal/repository/article"
	"gitee.com/DonHz/basic-go/webook/pkg/logger"
)

type ArticleService interface {
	Save(ctx context.Context, art domain.Article) (int64, error)
	Publish(ctx context.Context, art domain.Article) (int64, error)
	PublishV1(ctx context.Context, art domain.Article) (int64, error)
	Withdraw(ctx context.Context, art domain.Article) error
	List(ctx context.Context, art domain.Article, offset int, limit int) ([]domain.Article, error)
	GetById(ctx context.Context, uid int64) (domain.Article, error)
	GetPublishedById(ctx context.Context, aid, uid int64) (domain.Article, error)
}

type articleService struct {
	repo article.ArticleRepository
	// V1
	author   article.ArticleAuthorRepository
	reader   article.ArticleReaderRepository
	l        logger.Logger
	producer events.Producer
}

func NewArticleService(repo article.ArticleRepository, producer events.Producer, l logger.Logger) ArticleService {
	return &articleService{
		repo:     repo,
		l:        l,
		producer: producer,
	}
}

func NewArticleServiceV1(author article.ArticleAuthorRepository,
	reader article.ArticleReaderRepository, l logger.Logger) ArticleService {
	return &articleService{
		author: author,
		reader: reader,
		l:      l,
	}
}

func (a *articleService) Save(ctx context.Context, article domain.Article) (int64, error) {
	article.Status = domain.ArticleStatusUnpublished
	if article.Id > 0 {
		err := a.repo.Update(ctx, article)
		return article.Id, err
	}
	return a.repo.Create(ctx, article)
}

func (a *articleService) Publish(ctx context.Context, art domain.Article) (int64, error) {
	art.Status = domain.ArticleStatusPublished
	return a.repo.Sync(ctx, art)
}

func (a *articleService) PublishV1(ctx context.Context, art domain.Article) (int64, error) {
	var (
		id  = art.Id
		err error
	)
	if art.Id > 0 {
		err = a.author.Update(ctx, art)
	} else {
		id, err = a.author.Create(ctx, art)
	}
	if err != nil {
		return 0, err
	}
	art.Id = id
	// 万一制作库成功，保存到线上库失败，引入重试
	for i := 0; i < 3; i++ {
		id, err = a.reader.Save(ctx, art)
		if err == nil {
			break
		}
		a.l.Error("部分失败，保存到线上库失败",
			logger.Field{"article_id", art.Id},
			logger.Error(err))
	}
	if err != nil {
		a.l.Error("保存到线上库失败，重试失败",
			logger.Field{"article_id", art.Id},
			logger.Error(err))
		// 接入告警系统，手动导入
	}
	return id, err
}

func (a *articleService) Withdraw(ctx context.Context, art domain.Article) error {
	art.Status = domain.ArticleStatusPrivate
	return a.repo.SyncStatus(ctx, art)
}

func (a *articleService) List(ctx context.Context, art domain.Article, offset int, limit int) ([]domain.Article, error) {
	return a.repo.List(ctx, art, offset, limit)
}

func (a *articleService) GetById(ctx context.Context, uid int64) (domain.Article, error) {
	return a.repo.GetById(ctx, uid)
}

func (a *articleService) GetPublishedById(ctx context.Context, aid, uid int64) (domain.Article, error) {
	art, err := a.repo.GetPublishedById(ctx, aid)
	if err == nil {
		go func() {
			// 发生阅读事件
			er := a.producer.ProduceReadEvent(ctx,
				events.ReadEvent{
					Uid: uid,
					Aid: aid,
				})
			if er != nil {
				a.l.Error("发送读者阅读事件失败,",
					logger.Int64("uid", uid),
					logger.Int64("aid", aid))
			}
		}()
	}
	return art, err
}
